﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace Pavilion.GameTools
{
    public class NodeTool : GameToolBase
    {
        private static Dictionary<string, Action<NodeTool, object>> dataExecutionDictionary;

        public event Action<NodeScript, ContainerNodeMonitor> NodeMonitorAdded, NodeMonitorRemoved;
        public event Action<NodeScript, NodeBase> NodeLoaded, NodeUnloaded;

        public string ResourceName { get { return resourceName; } set { resourceName = value; LoadResource(); } }
        public Dictionary<NodeScript, ContainerNodeMonitor> ContainerNodeMonitorCollection { get { return containerNodeMonitorCollection; } }

        private Dictionary<NodeScript, ContainerNodeMonitor> containerNodeMonitorCollection;
        private ResourceItem resourceItem;
        private string resourceName;

        public NodeTool(GameItem parentGameItem) : base(parentGameItem)
        {
            containerNodeMonitorCollection = new Dictionary<NodeScript, ContainerNodeMonitor>();
            LoadDataExecution();
        }

        private void LoadDataExecution()
        {
            if (dataExecutionDictionary != null)
                return;

            dataExecutionDictionary = new Dictionary<string, Action<NodeTool, object>>();
            dataExecutionDictionary.Add("ResourceName", (nodeTool, value) => nodeTool.ResourceName = (string)value);
        }

        public override void UpdateProperty(string propertyName, object propertyValue)
        {
            dataExecutionDictionary[propertyName].Invoke(this, propertyValue);
        }

        private void LoadResource()
        {
            UnloadResource();

            try { resourceItem = ResourceManager.Instance[ResourceName]; }
            catch { return; }

            NodeScriptCollection nodeScriptCollection = resourceItem.ObtainResourceCollection<NodeScriptCollection>();

            foreach (NodeScript nodeScript in nodeScriptCollection)
                LoadNodeMontor(nodeScript);

            nodeScriptCollection.NodeScriptAdded += new Action<NodeScript>(NodeScriptCollection_NodeScriptAdded);
            nodeScriptCollection.NodeScriptRemoved += new Action<NodeScript>(NodeScriptCollection_NodeScriptRemoved);
        }

        private void UnloadResource()
        {
            if (resourceItem == null)
                return;

            while (ContainerNodeMonitorCollection.Count != 0)
                RemoveNodeMonitor(ContainerNodeMonitorCollection.Keys.First());

            NodeScriptCollection nodeScriptCollection = resourceItem.ObtainResourceCollection<NodeScriptCollection>();
            nodeScriptCollection.NodeScriptAdded -= new Action<NodeScript>(NodeScriptCollection_NodeScriptAdded);
            nodeScriptCollection.NodeScriptRemoved -= new Action<NodeScript>(NodeScriptCollection_NodeScriptRemoved);

            resourceItem = null;
        }

        private void LoadNodeMontor(NodeScript nodeScript)
        {
            AddNodeMonitor(nodeScript, new ContainerNodeMonitor(nodeScript, nodeScript.RootContainer, ParentGameItem, Logger.CreateLogger(ParentGameItem.Name + "." + nodeScript.Name)));
        }

        private void AddNodeMonitor(NodeScript nodeScript, ContainerNodeMonitor nodeMonitor)
        {
            ContainerNodeMonitorCollection.Add(nodeScript, nodeMonitor);
            nodeMonitor.BubbleUpNodeLoaded += new Action<ContainerNodeMonitor, NodeBase>(NodeMonitor_BubbleUpNodeLoaded);
            nodeMonitor.BubbleUpNodeUnloaded += new Action<ContainerNodeMonitor, NodeBase>(NodeMonitor_BubbleUpNodeUnloaded);
            OnNodeMonitorAdded(nodeScript, nodeMonitor);
            nodeMonitor.Start();
        }

        private void RemoveNodeMonitor(NodeScript nodeScript)
        {
            ContainerNodeMonitor nodeMonitor = ContainerNodeMonitorCollection[nodeScript];
            nodeMonitor.BubbleUpNodeLoaded -= new Action<ContainerNodeMonitor, NodeBase>(NodeMonitor_BubbleUpNodeLoaded);
            nodeMonitor.BubbleUpNodeUnloaded -= new Action<ContainerNodeMonitor, NodeBase>(NodeMonitor_BubbleUpNodeUnloaded);
            nodeMonitor.Stop();
            ContainerNodeMonitorCollection.Remove(nodeScript);
            OnNodeMonitorRemoved(nodeScript, nodeMonitor);
        }

        private void OnNodeMonitorAdded(NodeScript nodeScript, ContainerNodeMonitor nodeMonitor)
        {
            if (NodeMonitorAdded != null)
                NodeMonitorAdded(nodeScript, nodeMonitor);
        }

        private void OnNodeMonitorRemoved(NodeScript nodeScript, ContainerNodeMonitor nodeMonitor)
        {
            if (NodeMonitorRemoved != null)
                NodeMonitorRemoved(nodeScript, nodeMonitor);
        }

        private void OnNodeLoaded(NodeScript nodeScript, NodeBase node)
        {
            if (NodeLoaded != null)
                NodeLoaded(nodeScript, node);
        }

        private void OnNodeUnloaded(NodeScript nodeScript, NodeBase node)
        {
            if (NodeUnloaded != null)
                NodeUnloaded(nodeScript, node);
        }

        private void NodeScriptCollection_NodeScriptRemoved(NodeScript nodeScript)
        {
            RemoveNodeMonitor(nodeScript);
        }

        private void NodeScriptCollection_NodeScriptAdded(NodeScript nodeScript)
        {
            LoadNodeMontor(nodeScript);
        }

        private void NodeMonitor_BubbleUpNodeLoaded(ContainerNodeMonitor nodeMonitor, NodeBase node)
        {
            OnNodeLoaded(nodeMonitor.ParentNodeScript, node);
        }

        private void NodeMonitor_BubbleUpNodeUnloaded(ContainerNodeMonitor nodeMonitor, NodeBase node)
        {
            OnNodeUnloaded(nodeMonitor.ParentNodeScript, node);
        }
    }
}
